﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using DG.Common.EF.WPF;
using DG.Common.Patterns.Commands;
using DG.Common.Patterns.MVVM;
using TCC.ControleFluxoDeCaixa.Model.Entities;
using TCC.ControleFluxoDeCaixa.Model.Fluxo;
using TCC.ControleFluxoDeCaixa.Model.Fluxo.Credito;
using TCC.ControleFluxoDeCaixa.Model.Gerenciadores;

namespace TCC.ControleFluxoDeCaixa.Apresentacao.ViewModels.Credito
{
    public class LancamentoCartaoCreditoWindowViewModel : EntityCRUDViewModelBase<FluxoDeCaixaContextFiltrado, Lancamento>
    {
        public LancamentoCartaoCreditoWindowViewModel()
        {
            Validator.AddMethodValidator(() => ValorTotal == Parcelas.Sum(p => p.Movimento.ValorAbsoluto)
                , errorMessage: () => "O valor somado das parcerlas, não é igual ao valor da compra");
        }

        private decimal _valorTotal;
        private int _qtdParcelas;
        private string _descricao;

        


        protected override void AfterSetAction()
        {
            base.AfterSetAction();
            if (Action == CRUDOperationType.Update)
            {
                CarregarModoEdicao();
            }
            else if (Action == CRUDOperationType.Create)
            {
                Entity.DataDoLancamento = DateTime.Now;
                Entity.Usuario = Context.Usuarios.Find(AutenticadorDeSessao.GetInstance().CurrentSession.Usuario.UsuarioId);
            }
        }

        private void CarregarModoEdicao()
        {
            _contaCredito = (ContaCredito)Entity.Movimentos.First().Conta;
            _valorTotal = -Entity.Movimentos
                .Sum(m => (decimal?)((int)m.TipoDeMovimento) * m.ValorAbsoluto) ?? 0;
            _qtdParcelas = Entity.Movimentos.Count;
            _descricao = Entity.Nome;
            //Entity.Usuario = Context.GetUsuarioLogado();
        }

        public decimal ValorTotal
        {
            get { return _valorTotal; }
            set
            {
                if (value == _valorTotal) return;
                _valorTotal = value;
                OnPropertyChanged("ValorTotal");
            }
        }

        public int QtdParcelas
        {
            get { return _qtdParcelas; }
            set
            {
                if (value == _qtdParcelas) return;
                _qtdParcelas = value;
                OnPropertyChanged("QtdParcelas");
            }
        }

        public ContaCredito ContaCredito
        {
            get { return _contaCredito; }
            set
            {
                if (Equals(value, _contaCredito)) return;
                _contaCredito = value;
                OnPropertyChanged("ContaCredito");
                OnPropertyChanged("GerarCompraCredito");
            }
        }

        public string Descricao
        {
            get { return _descricao; }
            set
            {
                if (value == _descricao) return;
                _descricao = value;
                OnPropertyChanged("Descricao");
                OnPropertyChanged("GerarCompraCredito");
            }
        }

        public IEnumerable<ContaCredito> GetContasCredito 
        {
            get { return Context.ContasCredito.ToArray(); }
        }

        public IEnumerable<Categoria> GetCategorias
        {
            get { return Context.Categorias.ToArray(); }
        }

        private List<WrapperMovimento> _wrapperMovimentos;
        private ContaCredito _contaCredito;

        public IEnumerable<WrapperMovimento> Parcelas
        {
            get
            {
                if (_wrapperMovimentos != null) return _wrapperMovimentos;

                if (Entity != null)
                {
                    _wrapperMovimentos = Entity
                        .Movimentos.Select(movimento => new WrapperMovimento(movimento)).ToList();
                    return _wrapperMovimentos;
                }
                return null;
            }
        }
        public ICommand GerarCompraCredito
        {
            get
            {
                return new FunctionalCommand(() =>
                {

                    _wrapperMovimentos = null;
                    var movs = Entity.Movimentos.ToArray();
                    ContaCredito.LancamentoCartaoCredito(Entity, ValorTotal,  Entity.DataDoLancamento, QtdParcelas);
                    foreach (var mov in movs.Except(Entity.Movimentos).ToArray())
                    {
                        if (Context.Movimentos.Local.Contains(mov)) Context.Movimentos.Remove(mov);
                    }

                    var faturaCredito = Context.FaturasCredito.FirstOrDefault();
                    foreach (var mov in Entity.Movimentos)
                    {
                        if (faturaCredito != null && ((faturaCredito.Ano == mov.DataDoMovimento.Year && faturaCredito.Mes > mov.DataDoMovimento.Month) || faturaCredito.Ano > mov.DataDoMovimento.Year))
                        {
                            SendMessage("Lançamento em data Retroativa a primeira fatura quitada");
                            return;
                        }

                        var movClosure = mov;
                        var faturasQuitadas = Context.FaturasCredito
                            .Where(c => c.Ano == movClosure.DataDoMovimento.Year && c.Mes == movClosure.DataDoMovimento.Month)
                            .ToArray();
                        
                        if (faturasQuitadas.Any())
                        {
                            SendMessage("As seguintes faturas já foram quitadas: " 
                                + string.Join(",", faturasQuitadas.Select(f=> f.Ano + "/" + f.Mes.ToString("N2"))));
                            return;
                        }
                      
                    }
                    OnPropertyChanged("Parcelas");
                }, () =>
                    ContaCredito != null && Entity.Categoria != null && ValorTotal != 0);
            }
        }
    
        public ICommand RecalcularCommand
        {
            get
            {
                return new FunctionalCommand(() =>
                {
                    if ((Parcelas
                        .Where(c => c.NaoRecalcularParcela)
                        .ToList().Count) - Parcelas.Count() == 0)
                    {
                        SendMessage("Todas as parcelas foram modificadas manualmente");
                        return;
                    }
                    var novasParcelas = Parcelas.Where(c => c.NaoRecalcularParcela).ToList();

                    var divisor = (Parcelas.Count() - novasParcelas.Count());

                    var somaNovasParcelas = novasParcelas.Sum(p => p.Movimento.ValorAbsoluto);

                    decimal restante;
                    if (ValorTotal > somaNovasParcelas)
                    {
                        restante = (ValorTotal - somaNovasParcelas);
                    }
                    else
                    {
                        SendMessage("A soma das novas parcelas ultrapassa ou se iguala ao valor Total da compra");
                        return;
                    }
                        
                    var novaParcela = Arredondar(restante, divisor);
                    var resto = (ValorTotal - ((novaParcela * divisor) + somaNovasParcelas));
                    var restoSomado = false;
                    foreach (var movimento in Parcelas
                        .Where(c => c.NaoRecalcularParcela == false).Select(m=> m.Movimento))
                    {
                        if (!restoSomado)
                        {
                            movimento.ValorAbsoluto = novaParcela;
                            movimento.ValorAbsoluto += resto;
                            restoSomado = true;
                        }
                        else
                        {
                            movimento.ValorAbsoluto = novaParcela;
                        }
                    }
                    SendMessage("Parcelas Recalculadas com sucesso");
                },()=> Parcelas != null && Parcelas.Count() > 1 && 
                    !Parcelas.Any(p => p.Movimento.ValorAbsoluto == 0 && p.NaoRecalcularParcela));
            }
        }

        private static decimal Arredondar(decimal valorTemp, int divisor)
        {
            valorTemp = (valorTemp/divisor);
            valorTemp *= 100;
            valorTemp = Math.Floor(valorTemp);
            var novaParcela = (valorTemp/100);
            return novaParcela;
        }

    }

    public class WrapperMovimento : NotifyViewModel
    {
        private bool _naoRecalcularParcela;
        private Movimento _movimento;

        public Movimento Movimento
        {
            get { return _movimento; }
            private set
            {
                if (Equals(value, _movimento)) return;
                _movimento = value;
                OnPropertyChanged("Movimento");
                OnPropertyChanged("RecalcularCommand");
            }
        }

        public bool NaoRecalcularParcela
        {
            get { return _naoRecalcularParcela; }
            set
            {
                if (value.Equals(_naoRecalcularParcela)) return;
                _naoRecalcularParcela = value;
                OnPropertyChanged("NaoRecalcularParcela");
                OnPropertyChanged("RecalcularCommand");
            }
        }

        public WrapperMovimento(Movimento m)
        {
            Movimento = m;
            NaoRecalcularParcela = false;
        }

    }
}
